package atom

import (
	"errors"
	"fmt"
	"playbook/ginsvr/src/atom/mapper"
	"playbook/ginsvr/src/atom/wrapper"
	"playbook/ginsvr/src/orm"
	"playbook/ginsvr/src/orm/atomservice"
	"playbook/ginsvr/src/source/gene"
	"playbook/ginsvr/src/table"
	"playbook/ginsvr/src/utils/stringutil"
	"strings"

	"github.com/sirupsen/logrus"
)

var sql_key_words []string = []string{"eq", "ne", "gt", "ge", "lt", "le", "between", "notBetween",
	"like", "notLike", "likeLeft", "likeRight", "isNull", "isNotNull", "in", "notIn", "inSql",
	"notInSql", "groupBy", "orderByAsc", "orderByDesc", "exists", "notExists"}

const (
	delete_template  string = "DeleteAtom"
	update_template  string = "UpdateAtom"
	insert_template  string = "InsertAtom"
	select_template  string = "SelectAtom"
	sqlatom_template string = "SqlAtom"
)

type AtomParam struct {
	AtomName     string
	TableName    string
	AtomType     string
	WrapperParam wrapper.WapperParam
	AtomId       string
	SetParams    []SetParam
	SqlInfo      table.XmlInfo
	Param        map[string]interface{}
}

type SetParam struct {
	Property       string
	ValueType      string
	ValueExpresion string
}

func CreateAtomService(atomParam AtomParam) error {
	// 生成原子服务出入参
	atomSvr, err := atomservice.AddAtomParam(atomParam.Param)
	if err != nil {
		logrus.Error(err.Error())
		return err
	}
	atomParam.AtomId = atomSvr.Atomsvrid

	atomParam.AtomType = orm.GetCatagoryById(atomParam.AtomType).Intfabsname
	atomName := atomParam.AtomName
	if len(atomName) == 0 {
		logrus.Error("原子服务名称为空")
		return errors.New("原子服务名称为空")
	}
	if len(atomParam.TableName) == 0 {
		// 自定义sql场景
		// 1.生成Mapper类
		param := mapper.MapperParam{
			AtomName: atomParam.AtomName,
			AtomId:   atomParam.AtomId,
			SqlType:  atomParam.SqlInfo.SqlType,
		}
		err := mapper.GnenerateSqlMapper(param)
		if err != nil {
			logrus.Error("生成Mapper类失败")
			return err
		}
		// 2.生成xml文件
		err = table.GenerateXML(atomParam.SqlInfo, atomName, atomParam.AtomId)
		if err != nil {
			logrus.Error("生成MapperXml失败")
			return err
		}
		// 3.生成原子服务类
		return buildBySql(atomParam, atomSvr)
	} else {
		// 单表构建Wrapper场景
		return buildByWrapper(atomParam, atomSvr)
	}
}

func buildBySql(atomParam AtomParam, atomsvr orm.AtomSvrDefine) error {
	operatorType := atomParam.SqlInfo.SqlType
	if len(operatorType) == 0 {
		logrus.Error("操作类型为空")
		return errors.New("操作类型为空")
	}
	// 封装占位符替换
	var paramMap map[string]string = buildSqlVariableMap(atomParam, atomsvr)

	// 读取模板写出原子服务文件
	err := gene.GeneSrcByTpl(sqlatom_template, getCatagorySrcPath(atomsvr.Catagoryid), atomParam.AtomName+".java", paramMap)
	return err
}

func buildByWrapper(atomParam AtomParam, atomsvr orm.AtomSvrDefine) error {
	operatorType := atomParam.WrapperParam.OperationType
	if len(operatorType) == 0 {
		logrus.Error("操作类型为空")
		return errors.New("操作类型为空")
	}
	templateName := getTemplateName(operatorType)
	// 封装占位符替换
	var paramMap map[string]string = buildVariableMap(atomParam, atomsvr)
	// 读取模板写出原子服务文件
	err := gene.GeneSrcByTpl(templateName, getCatagorySrcPath(atomsvr.Catagoryid), atomParam.AtomName+".java", paramMap)
	return err
}

func buildAtomPath(atomName string, catagoryId string) string {
	localPath := orm.GetLocalPath("00001")
	var sb strings.Builder
	sb.WriteString(localPath.LocalPath)
	sb.WriteString("\\")
	sb.WriteString(getCatagorySrcPath(catagoryId))
	sb.WriteString("\\")
	sb.WriteString(atomName)
	sb.WriteString(".java")
	return sb.String()
}

func getCatagorySrcPath(catagoryId string) string {
	catagory := orm.GetCatagoryById(catagoryId)
	return catagory.Srcpath
}

func buildPackageName(pType string, catagoryId string) string {
	var packageName, srcPath string
	if pType == "atom" {
		srcPath = getCatagorySrcPath(catagoryId)
	} else {
		gsd := orm.GetGeneSrcDefine(pType)
		srcPath = gsd.Srcpath
	}
	packageName = srcPath[strings.Index(srcPath, "java")+5:]
	packageName = strings.ReplaceAll(packageName, "\\", ".")
	return packageName
}

func getTemplateName(operatorType string) string {
	switch operatorType {
	case wrapper.SELECT:
		return select_template
	case wrapper.UPDATE:
		return update_template
	case wrapper.INSERT:
		return insert_template
	case wrapper.DELETE:
		return delete_template
	}
	return ""
}

func buildVariableMap(atomParam AtomParam, atomSvrDefine orm.AtomSvrDefine) map[string]string {
	operatorType := atomParam.WrapperParam.OperationType
	var paramMap map[string]string = make(map[string]string)
	inputParam := orm.GetParamById(atomSvrDefine.Inputparamid)
	outputParam := orm.GetParamById(atomSvrDefine.Outputparamid)
	paramRelationOut, _ := orm.GetParameterbyId(atomSvrDefine.Outputparamid, "")
	fisrtSubParamOut := orm.GetParamById(paramRelationOut[0].Subparamid)

	paramRelationIn, _ := orm.GetParameterbyId(atomSvrDefine.Inputparamid, "")
	fisrtSubParamIn := orm.GetParamById(paramRelationIn[0].Subparamid)

	paramMap["packageName"] = buildPackageName("atom", atomSvrDefine.Catagoryid)

	paramMap["interfaceType"] = atomParam.AtomType
	paramMap["inputParameter"] = inputParam.Classpath
	paramMap["outputParameter"] = outputParam.Classpath
	paramMap["mapper"] = buildMapperClassPath(atomParam.TableName)
	paramMap["model"] = buildModelClassPath(atomParam.TableName)

	paramMap["interfaceTypeName"] = subName(atomParam.AtomType)
	paramMap["inputParameterName"] = subName(inputParam.Classpath)
	paramMap["outputParameterName"] = subName(outputParam.Classpath)
	paramMap["mapperName"] = subName(paramMap["mapper"])
	paramMap["modelName"] = subName(paramMap["model"])

	paramMap["atomName"] = atomParam.AtomName
	if operatorType != wrapper.INSERT {
		paramMap["wrapper"] = wrapper.BuildWrapper(atomParam.WrapperParam, atomSvrDefine, atomParam.TableName)
	}

	if operatorType == wrapper.SELECT {
		paramMap["respType"] = getRespType(subName(paramMap["model"]), fisrtSubParamOut)
		paramMap["mapperMethod"] = getMapperMethod(fisrtSubParamOut)
	}
	if paramRelationOut[0].Relname == "" {
		paramMap["dataName"] = stringutil.GetTitleStr(fisrtSubParamOut.Name)
	} else {
		paramMap["dataName"] = stringutil.GetTitleStr(paramRelationOut[0].Relname)
	}

	if paramRelationIn[0].Relname == "" {
		paramMap["dataNameIn"] = stringutil.GetTitleStr(fisrtSubParamIn.Name)
	} else {
		paramMap["dataNameIn"] = stringutil.GetTitleStr(paramRelationIn[0].Relname)
	}

	if operatorType == wrapper.UPDATE {
		paramMap["set"] = buildSetParam(atomParam, atomSvrDefine)
	}

	return paramMap
}

func buildMapperClassPath(tableName string) string {
	return buildPackageName("mapper", "") + "." + buildMapperName(tableName)
}

func buildSqlMapperClassPath(atomName string) string {
	return buildPackageName("mapper", "") + "." + atomName + "Mapper"
}

func buildModelClassPath(tableName string) string {
	return buildPackageName("model", "") + "." + buildModelName(tableName)
}

func buildMapperName(tableName string) string {
	var modelName string
	for _, c := range strings.Split(tableName, "_") {
		modelName = modelName + strings.ToUpper(c[:1]) + c[1:]
	}
	return modelName + "Mapper"
}

func buildModelName(tableName string) string {
	var modelName string
	for _, c := range strings.Split(tableName, "_") {
		modelName = modelName + strings.ToUpper(c[:1]) + c[1:]
	}
	return modelName + "Model"
}

func buildSqlVariableMap(atomParam AtomParam, atomSvrDefine orm.AtomSvrDefine) map[string]string {
	operatorType := atomParam.SqlInfo.SqlType
	var paramMap map[string]string = make(map[string]string)
	inputParam := orm.GetParamById(atomSvrDefine.Inputparamid)
	outputParam := orm.GetParamById(atomSvrDefine.Outputparamid)
	paramRelationOut, _ := orm.GetParameterbyId(atomSvrDefine.Outputparamid, "")
	fisrtSubParamOut := orm.GetParamById(paramRelationOut[0].Subparamid)

	paramMap["packageName"] = buildPackageName("atom", atomSvrDefine.Catagoryid)

	paramMap["interfaceType"] = atomParam.AtomType
	paramMap["inputParameter"] = inputParam.Classpath
	paramMap["outputParameter"] = outputParam.Classpath
	paramMap["mapper"] = buildSqlMapperClassPath(atomParam.AtomName)

	paramMap["interfaceTypeName"] = subName(atomParam.AtomType)
	paramMap["inputParameterName"] = subName(inputParam.Classpath)
	paramMap["outputParameterName"] = subName(outputParam.Classpath)
	paramMap["mapperName"] = subName(paramMap["mapper"])

	paramMap["atomName"] = atomParam.AtomName

	paramMap["methodName"] = strings.ToLower(operatorType)
	if paramRelationOut[0].Relname == "" {
		paramMap["dataName"] = stringutil.GetTitleStr(fisrtSubParamOut.Name)
	} else {
		paramMap["dataName"] = stringutil.GetTitleStr(paramRelationOut[0].Relname)
	}

	return paramMap
}

func buildSetParam(atomParam AtomParam, atomSvrDefine orm.AtomSvrDefine) string {
	setParams := atomParam.SetParams
	if setParams == nil || len(setParams) == 0 {
		return ""
	}
	fieldsMap := wrapper.GetFieldMap(atomParam.TableName)
	var sb strings.Builder
	for index, setParam := range setParams {
		setLine := fmt.Sprintf("model.set%s(%s);", stringutil.GetTitleStr(setParam.Property), getValue(setParam, atomSvrDefine, fieldsMap))
		if index > 0 {
			sb.WriteString(wrapper.FOUR_SPACING + wrapper.FOUR_SPACING)
		}
		sb.WriteString(setLine)
		sb.WriteString("\r\n")
	}

	return sb.String()
}

func getValue(setParam SetParam, atomSvrDefine orm.AtomSvrDefine, fieldsMap map[string]string) string {
	if wrapper.FIXED == setParam.ValueType {
		if "String" == fieldsMap[setParam.Property] {
			return wrapper.DOUBLE_QUOTATION + setParam.ValueExpresion + wrapper.DOUBLE_QUOTATION
		}
		return setParam.ValueExpresion
	}
	return wrapper.ConvertVlaueExpresion(strings.Split(setParam.ValueExpresion, wrapper.DOT), atomSvrDefine)
}

func subName(fullName string) string {
	return fullName[strings.LastIndex(fullName, ".")+1:]
}

func getRespType(modelName string, fisrtSubParam orm.Parameter) string {
	if "List" == fisrtSubParam.Classpath {
		return "List<" + modelName + ">"
	}
	return modelName
}

func getMapperMethod(fisrtSubParam orm.Parameter) string {
	if "List" == fisrtSubParam.Classpath {
		return "selectList"
	}
	return "selectOne"
}

func GetTableNames(projectId interface{}) []string {
	tableNames := orm.GetTableNames(projectId)
	return tableNames
}

// 关联表生成Model类与Mapper类
func GeneModelAndMapper(tableName string, fileds []*orm.Field) error {
	var err error
	primaryField := getPrimaryField(fileds)
	var tableInfo table.TableInfo
	tableInfo.TableName = tableName
	tableInfo.TableIdType = wrapper.DbTypeToJvavaType(primaryField.FieldType)
	tableInfo.TableIdName = primaryField.FieldName
	var tableFields []table.TableField = make([]table.TableField, 0)
	for _, tableField := range fileds {
		if tableField.IsPrimaryKey == "true" {
			continue
		}
		field := table.TableField{
			Type:   wrapper.DbTypeToJvavaType(tableField.FieldType),
			Column: tableField.FieldName,
		}
		tableFields = append(tableFields, field)
	}
	tableInfo.TableFields = tableFields

	// 生成Model类
	err = table.GenerateModel(tableInfo)
	if err != nil {
		logrus.Error("生成Model类失败")
		return err
	}
	// 生成Mapper类
	err = mapper.GenerateMapper(tableName)
	if err != nil {
		logrus.Error("生成Mapper类失败")
		return err
	}
	return nil
}

func getPrimaryField(fileds []*orm.Field) orm.Field {
	var field orm.Field
	for _, tableField := range fileds {
		if tableField.IsPrimaryKey == "true" {
			field = *tableField
			break
		}
	}
	return field
}

func GetFeildNames(projectId string, tablename string) []string {
	return orm.GetFeildNames(projectId, tablename)
}

func GetSqlKeywords() []string {
	return sql_key_words
}

func GetResultMap(sql string) []string {
	lowerSql := strings.ToLower(sql)
	lowerSql = lowerSql[strings.Index(lowerSql, "select")+6 : strings.Index(lowerSql, "from")]
	lowerSql = trim(lowerSql)
	fields := strings.Split(lowerSql, wrapper.COMMA)
	var resultMap []string = make([]string, 0)
	for _, field := range fields {
		if strings.Contains(field, wrapper.DOT) {
			resultMap = append(resultMap, field[strings.Index(field, wrapper.DOT)+1:])
		} else {
			resultMap = append(resultMap, field)
		}
	}
	return resultMap
}

func trim(src string) (dist string) {
	if len(src) == 0 {
		return
	}

	r, distR := []rune(src), []rune{}
	for i := 0; i < len(r); i++ {
		if r[i] == 10 || r[i] == 32 {
			continue
		}
		distR = append(distR, r[i])
	}

	dist = string(distR)
	return
}
